<!DOCTYPE html>
<html>
    <head>

        <link rel="canonical" href="http://www.jointjs.com/" />
        <meta name="description" content="Create interactive diagrams in JavaScript easily. JointJS plugins for ERD, Org chart, FSA, UML, PN, DEVS, LDM diagrams are ready to use." />
        <meta name="keywords" content="JointJS, JavaScript, diagrams, diagramming library, UML, charts" />

        <link href="http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" rel="stylesheet" type="text/css" />
        <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">

        <link rel="stylesheet" href="css/tutorial.css" />
        <link rel="stylesheet" href="../node_modules/prismjs/themes/prism.css">

        <!-- Dependencies: -->
        <script src="../node_modules/jquery/dist/jquery.js"></script>
        <script src="../node_modules/lodash/lodash.js"></script>
        <script src="../node_modules/backbone/backbone.js"></script>

        <link rel="stylesheet" type="text/css" href="../build/joint.min.css" />
        <script type="text/javascript" src="../build/joint.min.js"></script>

        <title>JointJS - JavaScript diagramming library - Getting started.</title>

    </head>
    <body class="language-javascript tutorial-page">

        <script>
            SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(toElement) {
                return toElement.getScreenCTM().inverse().multiply(this.getScreenCTM());
            };
        </script>

        <div class="tutorial">

            <h2>Events</h2>

            <p>This is the second article of the intermediate section of the JointJS tutorial.
                <a href="special-attributes.html">Go back to special attributes.</a>
                Alternatively, you can <a href="intermediate.html">return to index</a> of all articles.</p>

            <p>JointJS offers several different ways to enable user interaction.
                Events are triggered on your paper, on individual element/link views.</p>

            <h3 id="paper-events">Builtin Paper Events</h3>

            <p>Paper automatically triggers several builtin events upon user interaction.
                These include pointerdown, double click and right click events, as well as link connection or cell
                highlighting events.
                You can find the full list in <a href="/docs/jointjs#dia.Paper.events" target="_blank">Paper
                documentation</a>.
                Reacting on any of these events is as simple as adding a listener on your paper; this is the easiest way
                to detect one type of interaction on all instances of a component.</p>

            <p>For example, the event <code>'link:pointerdblclick'</code> is triggered when a double click is detected
                on any link in the diagram.
                A corresponding event for elements (<code>'element:pointerdblclick'</code>), cells in general
                (<code>'cell:pointerdblclick'</code>), and for blank areas on the paper
                (<code>'blank:pointerdblclick'</code>) is provided as well,
                Here, the cell event listener unhides an element with a message whenever an Element or Link is clicked.
                The more specific element and link event listeners change the color of their model's stroke.
                Finally, the blank event listener hides the message element and changes the color of the Paper
                background:</p>

            <div class="paper" id="paper-events-paper-events"></div>

            <pre><code>paper.on('blank:pointerdblclick', function() {
    resetAll(this);

    info.attr('body/visibility', 'hidden');
    info.attr('label/visibility', 'hidden');

    this.drawBackground({
        color: 'orange'
    })
});

paper.on('element:pointerdblclick', function(elementView) {
    resetAll(this);

    var currentElement = elementView.model;
    currentElement.attr('body/stroke', 'orange')
});

paper.on('link:pointerdblclick', function(linkView) {
    resetAll(this);

    var currentLink = linkView.model;
    currentLink.attr('line/stroke', 'orange')
    currentLink.label(0, {
        attrs: {
            body: {
                stroke: 'orange'
            }
        }
    })
});

paper.on('cell:pointerdblclick', function(cellView) {
    var isElement = cellView.model.isElement();
    var message = (isElement ? 'Element' : 'Link') + ' clicked';
    info.attr('label/text', message);

    info.attr('body/visibility', 'visible');
    info.attr('label/visibility', 'visible');
});

function resetAll(paper) {
    paper.drawBackground({
        color: 'white'
    })

    var elements = paper.model.getElements();
    for (var i = 0, ii = elements.length; i < ii; i++) {
        var currentElement = elements[i];
        currentElement.attr('body/stroke', 'black');
    }

    var links = paper.model.getLinks();
    for (var j = 0, jj = links.length; j < jj; j++) {
        var currentLink = links[j];
        currentLink.attr('line/stroke', 'black');
        currentLink.label(0, {
            attrs: {
                body: {
                    stroke: 'black'
                }
            }
        })
    }
}</code></pre>

            <p>JointJS source code: <a href="js/events-paper-events.js" target="_blank">events-paper-events.js</a></p>

            <p>The event listener callback functions have the signature
                <code>callback([cellView,] eventObject, eventX, eventY)</code> (<code>cellView</code> is not provided
                for <code>'blank:…'</code> events, for obvious reasons).
                Inside the listeners, the paper itself is available as <code>this</code>.
                The <code>eventObject</code> can be used to, for example, <code>stopPropagation()</code>.
                The <code>x</code> and <code>y</code> coordinates can be useful when placing markers at sites of user
                interaction.
                We have not used these three parameters in the above example, but you will encounter them frequently in
                JointJS event listeners.</p>

            <p>Note that all of these events are also triggered on the individual <code>cellView</code> (element or link
                view) the user interacted with.
                Therefore, you could achieve the same functionality as above by adding a listener on every elementView
                and linkView separately.
                While this approach has its uses, we recommend using paper listeners; having a single listener cover all
                interaction options is better practice in JavaScript than having dozens of listeners for individual
                views.</p>

            <h3 id="graph-events">Builtin Graph Events</h3>

            <p>The Graph model comes with several builtin events as well.
                These events react to changes in the cell model's properties, including position, size, attributes, and
                status of JointJS transitions.
                JointJS documentation contains a generic list of
                <a href="/docs/jointjs#dia.Graph.events" target="_blank">Graph events</a> you can react on, alongside
                more specific lists for <a href="/docs/jointjs#dia.Element.events" target="_blank">Element events</a>
                and <a href="/docs/jointjs#dia.Link.events" target="_blank">Link events</a>.
                To react on these events, add a listener on your Graph model.</p>

            <p>In the following example, the event <code>'change:position'</code> is triggered on the element when it is
                moved; we determine the new position of the element's center and write it into its text label.
                The event <code>'change:target'</code> is triggered on the link when its target is moved; we use that
                event to write the new target position into the link label.</p>

            <div class="paper" id="paper-events-graph-events"></div>

            <pre><code>graph.on('change:position', function(cell) {
    var center = cell.getBBox().center();
    var label = center.toString();
    cell.attr('label/text', label);
});

graph.on('change:target', function(cell) {
    var target = new g.Point(cell.target());
    var label = target.toString();
    cell.label(0, {
        attrs: {
            label: {
                text: label
            }
        }
    });
});</code></pre>

            <p>JointJS source code: <a href="js/events-graph-events.js" target="_blank">events-graph-events.js</a></p>

            <p>The callback functions of the cell graph change events have the signature
                <code>callback(cell, newValue)</code>.
                Inside the listeners, the graph itself is available as <code>this</code>.
                In our example, we were able to assume that the received <code>cell</code> model is of the type Element
                and Link, respectively, because <code>Link</code> objects do not trigger <code>'change:position'</code>
                events, and <code>Element</code> objects do not trigger <code>'change:target'</code>.
                Keep in mind that some Graph events can be triggered on both data types, however (e.g.
                <code>'change:attrs'</code>); depending on what you are trying to do, you might need to check the actual
                <code>cell</code> data type first.</p>

            <p>Other graph event listeners are provided with different parameters.
                The generic <code>'graph:change'</code> event listeners receive only the changed cell but not the new
                value (<code>callback(cell)</code>).
                The <code>'graph:add'</code> event listeners receive the added cell and the <i>updated</i> cells array
                (<code>callback(cell, newCells)</code>), whereas the <code>'graph:remove'</code> event listeners receive
                the removed cell and the <i>original</i> cells array (<code>callback(cell, oldCells)</code>).</p>

            <p>Similarly to builtin paper events, these events are also triggered on the individual <code>cell</code>
                (element or link model) the user interacted with.
                Therefore, you could achieve the same functionality as above by adding a listener on every element and
                link separately.
                While this approach has its uses, we recommend using graph listeners; having a single listener cover all
                interaction options is better practice in JavaScript than having dozens of listeners for individual
                models.</p>

            <p>The graph can also react on changes in its own properties.
                For example, calling <code>graph.set('property', true)</code> would trigger
                <code>'change:property'</code> on the <code>graph</code>).
                Event listeners for graph attribute changes receive a reference to the graph and the new value as their
                parameters (<code>callback(graph, newValue)</code>).</p>

            <p>Beware that due to backwards-compatibility considerations, this can lead to confusion if we are careless
                in choosing custom graph property names!
                If we named our custom graph property <code>position</code> instead of <code>property</code>, the
                triggered event would be identified as <code>'change:position'</code>, and would thus be captured by the
                event listener in our example - but the callback would have to contend with an unexpected set of
                arguments.
                To avoid such name collisions, we strongly recommend adopting a naming convention for custom graph
                properties - e.g. starting their variable names with the word <q>graph</q> (i.e.
                <code>graphProperty</code> and <code>graphPosition</code>).
                If that is not an option, and you need to support custom graph attributes, you can make yourself safe by
                asserting that the <code>cell</code> parameter is not actually a <code>Graph</code>:</p>

            <pre><code>graph.on('change:position', function(cell) {
    if (cell instanceof joint.dia.Graph) return;
    var center = cell.getBBox().center();
    var label = center.toString();
    cell.attr('label/text', label);
});</code></pre>

            <h3 id="event-attribute">Subelement Event Attribute</h3>

            <p>The easiest way to add a custom event to an individual component on your diagram is to use the
                <code>event</code> <a href="special-attributes.html" target="_blank">special attribute</a>.</p>

            <ul>
                <li><a href="/docs/jointjs#dia.attributes.event" target="_blank"><code>event</code></a> - adds an event
                    of the specified name to the subelement's model; to be triggered when JointJS detects a pointerdown
                    event (mousedown/touchstart DOM event) on the subelement.</li>
            </ul>

            <p>This special attribute is useful for creating elements with custom tool subelements (e.g. a minimize
                button).
                Then, you can simply attach a listener on your paper that gets called when your custom event is
                detected.</p>

            <p>In the following example, we define a <a href="custom-elements.html">custom element type</a> with a
                <code>button</code> subelement.
                We attach a custom event <code>'element:button:pointerdown'</code> to the <code>button</code> and listen
                for it.
                When <code>button</code> is pressed, the element's <code>body</code> and <code>label</code> are hidden
                (<q>minimized</q>), and the symbol in the button is changed to indicate that the element can be now be
                unminimized:</p>

            <div class="paper" id="paper-events-event-attribute"></div>

            <pre><code>var CustomElement = joint.dia.Element.define('examples.CustomElement', {
    attrs: {
        body: {
            refWidth: '100%',
            refHeight: '100%',
            strokeWidth: 2,
            stroke: 'black',
            fill: 'white'
        },
        label: {
            textVerticalAnchor: 'middle',
            textAnchor: 'middle',
            refX: '50%',
            refY: '50%',
            fontSize: 14,
            fill: 'black'
        },
        button: {
            cursor: 'pointer',
            ref: 'buttonLabel',
            refWidth: '150%',
            refHeight: '150%',
            refX: '-25%',
            refY: '-25%'
        },
        buttonLabel: {
            pointerEvents: 'none',
            refX: '100%',
            refY: 0,
            textAnchor: 'middle',
            textVerticalAnchor: 'middle'
        }
    }
}, {
    markup: [{
        tagName: 'rect',
        selector: 'body',
    }, {
        tagName: 'text',
        selector: 'label'
    }, {
        tagName: 'rect',
        selector: 'button'
    }, {
        tagName: 'text',
        selector: 'buttonLabel'
    }]
});

var element = new CustomElement();
element.position(250, 30);
element.resize(100, 40);
element.attr({
    label: {
        pointerEvents: 'none',
        visibility: 'visible',
        text: 'Element'
    },
    body: {
        cursor: 'default',
        visibility: 'visible'
    },
    button: {
        event: 'element:button:pointerdown',
        fill: 'orange',
        stroke: 'black',
        strokeWidth: 2
    },
    buttonLabel: {
        text: '＿', // fullwidth underscore
        fill: 'black',
        fontSize: 8,
        fontWeight: 'bold'
    }
});
element.addTo(graph);

paper.on('element:button:pointerdown', function(elementView, evt) {
    evt.stopPropagation(); // stop any further actions with the element view (e.g. dragging)

    var model = elementView.model;

    if (model.attr('body/visibility') === 'visible') {
        model.attr('body/visibility', 'hidden');
        model.attr('label/visibility', 'hidden');
        model.attr('buttonLabel/text', '＋'); // fullwidth plus

    } else {
        model.attr('body/visibility', 'visible');
        model.attr('label/visibility', 'visible');
        model.attr('buttonLabel/text', '＿'); // fullwidth underscore
    }
});</code></pre>

            <p>JointJS source code: <a href="js/events-event-attribute.js" target="_blank">events-event-attribute.js</a></p>

            <h3 id="custom-view-events">Custom View Events</h3>

            <p>For more advanced event customization, we need to delve into custom views.
                That is an advanced topic with many powerful options; here we will concentrate on extending our View
                objects with custom events.</p>

            <p>The Paper object has two options that determine the views used to render diagram components:</p>

            <ul>
                <li><a href="/docs/jointjs#dia.Paper.prototype.options.elementView" target="_blank"><code>elementView</code></a>
                    - sets the ElementView to use to render Element models on this paper.
                    Defaults to
                    <a href="/docs/jointjs#dia.ElementView" target="_blank"><code>joint.dia.ElementView</code></a>.</li>
                <li><a href="/docs/jointjs#dia.Paper.prototype.options.linkView" target="_blank"><code>linkView</code></a>
                    - sets the LinkView to use to render Link models. Defaults to
                    <a href="/docs/jointjs#dia.LinkView" target="_blank"><code>joint.dia.LinkView</code></a>.</li>
            </ul>

            <p>We will use these two options to provide extended versions of the default ElementView and LinkView.
                The example removes double-clicked components:</p>

            <div class="paper" id="paper-events-custom-view-events"></div>

            <pre><code>var paper = new joint.dia.Paper({
    el: document.getElementById('paper-custom-view-events'),
    model: graph,
    width: 600,
    height: 100,
    gridSize: 1,
    background: {
        color: 'white'
    },
    interactive: false, // disable default interaction (e.g. dragging)
    elementView: joint.dia.ElementView.extend({
        pointerdblclick: function(evt, x, y) {
            this.model.remove();
        }
    }),
    linkView: joint.dia.LinkView.extend({
        pointerdblclick: function(evt, x, y) {
            this.model.remove();
        }
    })
});</code></pre>

            <p>JointJS source code: <a href="js/events-custom-view-events.js" target="_blank">events-custom-view-events.js</a></p>

            <h3 id="custom-view-events-propagation">Custom View Event Propagation</h3>

            <p>You can maintain recognition of the event by the <a href="#paper-events">builtin paper mechanism</a>
                if you notify the CellView and Paper about the event.
                For example, in our custom ElementView pointerdblclick handler, we would include this:</p>

            <pre><code>joint.dia.CellView.prototype.pointerdblclick.apply(this, arguments);
this.notify('element:pointerdblclick', evt, x, y);</code></pre>

            <p>This can be useful if you need to maintain default event handling behavior.
                The following example integrates showing a message element on Cell click from the
                <a href="#paper-events">paper events demo</a> with removing the components from the previous
                example:</p>

            <div class="paper" id="paper-events-custom-view-events-propagation"></div>

            <pre><code>var paper = new joint.dia.Paper({
    el: document.getElementById('paper-custom-view-events'),
    model: graph,
    width: 600,
    height: 100,
    gridSize: 1,
    background: {
        color: 'white'
    },
    interactive: false, // disable default interaction (e.g. dragging)
    elementView: joint.dia.ElementView.extend({
        pointerdblclick: function(evt, x, y) {
            joint.dia.CellView.prototype.pointerdblclick.apply(this, arguments);
            this.notify('element:pointerdblclick', evt, x, y);
            this.model.remove();
        }
    }),
    linkView: joint.dia.LinkView.extend({
        pointerdblclick: function(evt, x, y) {
            joint.dia.CellView.prototype.pointerdblclick.apply(this, arguments);
            this.notify('link:pointerdblclick', evt, x, y);
            this.model.remove();
        }
    })
});

paper.on('cell:pointerdblclick', function(cellView) {
    var isElement = cellView.model.isElement();
    var message = (isElement ? 'Element' : 'Link') + ' removed';
    info.attr('label/text', message);

    info.attr('body/visibility', 'visible');
    info.attr('label/visibility', 'visible');
});</code></pre>

            <p>JointJS source code: <a href="js/events-custom-view-events-propagation.js" target="_blank">events-custom-view-events-propagation.js</a></p>

            <p><a href="serialization.html">In the next section of the intermediate tutorial, we will learn about data
                serialization.</a></p>

        </div><!--end tutorial-->

        <script src="../node_modules/prismjs/prism.js"></script>

        <script src="js/events-paper-events.js"></script>
        <script src="js/events-graph-events.js"></script>
        <script src="js/events-event-attribute.js"></script>
        <script src="js/events-custom-view-events.js"></script>
        <script src="js/events-custom-view-events-propagation.js"></script>
    </body>
</html>
